﻿/**********************************************************************
 * Base.cs
 * 
 * define the BaseParser class that all other Parser will derive from.
 * 
 **********************************************************************
 * yc 11/22/2011 - first version
 **********************************************************************/
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO; 
using System.Text;

namespace Parseq.Core
{
	/// <summary>
	/// Transform delegate. 
	/// 
	/// object -> object 
	/// 
	/// used for transforming during the parsing stage.
	/// </summary>
    public delegate object TransformDelegate(object from);
	
	/// <summary>
	/// Transform array delegate.
	/// 
	/// object[] -> object 
	/// 
	/// used for transforming during the parsing stage, specifically when we know the 
	/// incoming object is an array of objects. 
	/// </summary>
    public delegate object TransformArrayDelegate(object[] from); 
	
	/// <summary>
	/// Base parser. This is the base class for the rest of parser combinators. 
	/// </summary>
	/// <exception cref='InvalidCastException'>
	/// Is thrown when an explicit conversion (casting operation) fails because the source type cannot be converted to the
	/// destination type.
	/// </exception>
    public abstract class BaseParser 
    {
        private TransformDelegate transform = delegate(object o) { return o; };

        public abstract Result InnerParse(IReader reader, Location pos);

        public Result Parse(IReader reader, Location pos)
        {
            Result temp = this.InnerParse(reader, pos);
            if (Result.Succeeded(temp))
            {
                return new Result(this.InnerTransform(temp.Inner), temp.Position);
            }
            else
            {
                return temp; 
            }
        }

        public virtual object InnerTransform(object val)
        {
            if (this.transform != null)
            {
                return this.transform(val); 
            }
            else
            {
                return val; 
            }
        }

        public BaseParser SetTransform(TransformDelegate transform)
        {
            this.transform = transform;
            return this; 
        }

        public BaseParser SetTransformArray(TransformArrayDelegate transform)
        {
            return this.SetTransform(ObjectToObjectArray(transform));
        }

		
		/// <summary>
		/// Atoms to string. Converting a list of object into a single string recursively. 
		/// </summary>
		/// <returns>
		/// The to string.
		/// </returns>
		/// <param name='objects'>
		/// Objects.
		/// </param>
		/// <exception cref='InvalidCastException'>
		/// Is thrown when an explicit conversion (casting operation) fails because the source type cannot be converted to the
		/// destination type.
		/// </exception>
        public static string AtomToString(object[] objects)
        {
	        // Lexer2s will transform things to atom - char, long, double, byte, bytes 
	        // the idea is that we need to transform things to char array 
            string[] results = new string[objects.Length];
            for (long i = 0; i < results.Length; ++i)
            {
                if (objects[i] is char)
                {
                    results[i] = new string((char)objects[i], 1);
                } else if (objects[i] is string) {
                    results[i] = (string)objects[i];
                }
                else if (objects[i] is char[])
                {
                    results[i] = new string((char[])objects[i]);
                }
                else if (objects[i] is long)
                {
                    results[i] = objects[i].ToString();
                }
                else if (objects[i] is double)
                {
                    results[i] = objects[i].ToString();
                } else if (objects[i] is object[]) {
                    // we need to call ourselves recursively.
                    results[i] = AtomToString((object[])objects[i]); 
                }
                else
                {
                    throw new InvalidCastException(String.Format("invalid argument: {0}", objects[i])); 
                }
            }
            string s = String.Join("", results);
            return s; 
        }

        public static TransformDelegate ObjectToObjectArray(TransformArrayDelegate arrayTransform)
        {
            return delegate(object o)
            {
                return arrayTransform((object[])o);
            };
        }

        public static object ObjectArrayJoin(object[] ary)
        {
            return ObjectArrayJoin(ary, typeof(object)); 
        }

        public static object ObjectArrayJoin(object[] ary, Type type)
        {
            ArrayList list = new ArrayList(); 
            foreach (object obj in ary) {
                if (obj is Array) {
                    list.AddRange((Array)obj);
                } else {
                    list.Add(obj);
                }
            }
            return list.ToArray(type);
        }
    }
    public delegate Result ParserDelegate(IReader reader, Location pos);

    public class DelegateParser : BaseParser // because we cannot subclass a delegate we'll have to use class interface instead 
    {
        private ParserDelegate inner;

        public DelegateParser(ParserDelegate inner)
        {
            this.inner = inner;
        }

        public override Result InnerParse(IReader reader, Location pos)
        {
            return this.inner(reader, pos);
        }

        public static DelegateParser WrapDelegate(ParserDelegate parser)
        {
            return new DelegateParser(parser);
        }

        public static ParserDelegate WrapParser(BaseParser parser)
        {
            return delegate(IReader reader, Location pos)
            {
                return parser.Parse(reader, pos);
            };
        }
    } //*/
}
